package com.liyu.iblog.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.liyu.iblog.dao.BlogDao;
import com.liyu.iblog.dao.CategoryDao;
import com.liyu.iblog.dao.TagDao;
import com.liyu.iblog.domain.Blog;
import com.liyu.iblog.domain.Category;
import com.liyu.iblog.domain.Tag;
import com.liyu.iblog.exception.BlogNotFoundException;
import com.liyu.iblog.utils.StringUtils;

@Service("blogService")
public class BlogServiceImpl implements BlogService {

    private BlogDao blogDao;
    private CategoryDao categoryDao;
    private TagDao tagDao;
    
    @Autowired
    public BlogServiceImpl(BlogDao blogDao, CategoryDao categoryDao, TagDao tagDao) {
        this.blogDao = blogDao;
        this.categoryDao = categoryDao;
        this.tagDao = tagDao;
    }
    
    @Override
    public void create(Blog blog) {
        preProcessBlog(blog);
        if (blog.getTagsString() != null && !blog.getTagsString().trim().isEmpty()) {
            blog.setTagsString(blog.getTagsString().replaceAll("\\s+", " "));
            processTags(blog);
        }
        if (blog.getCategoryKey() != null) {
//            Category category = categoryDao.get(blog.getCategoryKey());
//            category.getBlogKeys().add(blog.getKey());
//            categoryDao.save(category);
            categoryDao.updateCategoryForNewBlog(blog);
        } else {
            //if category not specified categorize the blog to the default category 'blog' 
            Category category = categoryDao.get(CategoryService.DEFAULT_CATEGORY_NAME.toLowerCase());
            if (category == null) {
                category = new Category();
                category.setName(CategoryService.DEFAULT_CATEGORY_NAME);
                category.setId(category.getName().toLowerCase());
                Key key = KeyFactory.createKey(Category.class.getSimpleName(), category.getId());
                category.setKey(key);
            }
            category.addBlogKey(blog.getKey());
            categoryDao.save(category);
        }
        blogDao.save(blog);
    }
    
    private void preProcessBlog(Blog blog) {
        //generate hyphen separated id
        blog.setId(StringUtils.transformToHyphenSeparatedString(blog.getTitle()));
        //generate key from id
        blog.setKey(KeyFactory.createKey(Blog.class.getSimpleName(), blog.getId()));
        //the reference to the publisher of the blog
        blog.setUserKey(BlogService.DEFAULT_BLOG_OWNER);
        //make the summary of the blog content
        blog.setSummary(StringUtils.makeSummary(blog.getContent()));
    }
    
    // handle the tags
    private void processTags(Blog blog) {
        String[] tags = blog.getTagsString().split(",");
        Tag tag = null;
        String tagKeyString = null;
        for (String tagName : tags) {
            tagName = tagName.trim();
            tagKeyString = StringUtils.transformToHyphenSeparatedString(tagName);
            tag = tagDao.get(tagKeyString);
            // if tag not exits in the system
            if (tag == null) {
                tag = new Tag();
                tag.setName(tagName);
                tag.setId(tagKeyString);
            }
            tag.addBlogKey(blog.getKey());
            tagDao.save(tag);
            blog.addTag(tag.getId());
        }
    }
    
    @Override
    public Blog getBlogById(String id) {
        Key key = KeyFactory.createKey(Blog.class.getSimpleName(), id);
        return blogDao.get(key);
    }
    
    @Override
    public Blog getBlogWithComment(String id) throws BlogNotFoundException {
        Key key = KeyFactory.createKey(Blog.class.getSimpleName(), id);
        Blog blog = blogDao.getBlogWithComments(key);
        if (blog == null)
            throw new BlogNotFoundException(id);
        return blog;
    }
    
    @Override
    public List<Blog> getAllBlogs() {
        return blogDao.getAll();
    }
    
    @Override
    public List<Blog> getBolgsWithTimeDescOrder(int firstIndex, int batchSize) {
        return blogDao.getBlogsWithTimeDescOrder(firstIndex, batchSize, BlogService.DEFAULT_BLOG_OWNER);
    }

    @Override
    public List<Blog> getBlogsByCategory(Object categoryId, int firstIndex,
            int batchSize) {
        return blogDao.getBlogsByCategory(categoryId, firstIndex, batchSize);
    }

    @Override
    public List<Blog> getBlogsByTag(String tagId, int firstIndex, int batchSize) {
        return blogDao.getBlogByTag(tagId, firstIndex, batchSize);
    }
    
    /**
     * update the content of a blog, 
     * change the category are not encouraged(the order is hard to maintain)
     * remove then add the same tag again are not encouraged
     */
    @Override
    public void updateBlog(Object oldBlodId, Blog newBlog) {

        Blog oldBlog = blogDao.get(oldBlodId);
        if (oldBlog == null)
            return;
        
        //if title of the blog changed
        if (!newBlog.getTitle().equals(oldBlog.getTitle())) {
            oldBlog.setTitle(newBlog.getTitle());
        }
        
        //category changed, rebuild category
        if (!newBlog.getCategoryKey().equals(oldBlog.getCategoryKey())) {
            Category oldCategory = categoryDao.get(oldBlog.getCategoryKey());
            oldCategory.getBlogKeys().remove(oldBlog.getKey());
            categoryDao.save(oldCategory);
            Category newCategory = categoryDao.get(newBlog.getCategoryKey());
            newCategory.addBlogKey(newBlog.getKey());
            categoryDao.save(newCategory);
            oldBlog.setCategoryKey(newCategory.getId());
        }
        
        newBlog.clearTags();
        //tags changed, rebuild tags
        if (!newBlog.getTagsString().equals(oldBlog.getTagsString())) {
            List<String> oldTagKeys = oldBlog.getTags();
            //if new blog removed all tags
            if (newBlog.getTagsString() == null
                    || newBlog.getTagsString().trim().isEmpty()) {
                Tag tag = null;
                for (String tagKeyStr : oldTagKeys) {
                    tag = tagDao.get(tagKeyStr);
                    if (tag != null) {
                        tag.removeBlogKey(oldBlog.getKey());
                        tagDao.save(tag);
                    }
                }
            } else {
                newBlog.setTagsString(newBlog.getTagsString().replaceAll(
                        "\\s+", " "));
                String[] newTagStrings = newBlog.getTagsString().split(",");
                String tagKeyString = null;
                Tag tag = null;
                // add new tag not contains in the old blog
                for (String tagName : newTagStrings) {
                    tagName = tagName.trim();
                    tagKeyString = StringUtils
                            .transformToHyphenSeparatedString(tagName);
                    newBlog.addTag(tagKeyString);
                    // if the new tags contains tag not exists in the old tags
                    if (!oldTagKeys.contains(tagKeyString)) {
                        tag = tagDao.get(tagKeyString);
                        if (tag == null) {
                            tag = new Tag();
                            tag.setName(tagName);
                            tag.setId(tagKeyString);
                        }
                        tag.addBlogKey(oldBlog.getKey());
                        tagDao.save(tag);
                    }
                }
                // remove tag deleted from old blog
                for (String tagId : oldBlog.getTags()) {
                    if (!newBlog.getTags().contains(tagId)) {
                        tag = tagDao.get(tagId);
                        if (tag == null)
                            continue;
                        tag.removeBlogKey(oldBlog.getKey());
                        tagDao.save(tag);
                    }
                }
            }
            oldBlog.setTagsString(newBlog.getTagsString());
            oldBlog.setTags(newBlog.getTags());
        }
        //in case, the content have been changed 
        oldBlog.setContent(newBlog.getContent());
        oldBlog.setSummary(StringUtils.makeSummary(oldBlog.getContent()));
        blogDao.save(oldBlog);
    }
    
    @Override
    public void deleteBlog(Object id) {
        Blog blog = blogDao.get(id);
        if (blog == null)
            return;
        Key blogKey = KeyFactory.createKey(Blog.class.getSimpleName(), blog.getId());
//        Category category = categoryDao.get(blog.getCategoryKey());
//        if (category != null) {
//            category.getBlogKeys().remove(blogKey);
//            categoryDao.save(category);
//        }
        categoryDao.updateCategoryForRemovedBlog(blog.getCategoryKey(), blogKey);
        
        List<String> tagKeys = blog.getTags();
        if (tagKeys != null) {
            Tag tag = null;
            for (String tagKeyStr : tagKeys) {
                tag = tagDao.get(tagKeyStr);
                if (tag != null) {
                    tag.getBlogKeys().remove(blogKey);
                    tagDao.save(tag);
                }
            }
        }
        
        blogDao.delete(id);
    }
}
